<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>分片上传测试</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
    }
    .container {
      border: 1px solid #ddd;
      border-radius: 8px;
      padding: 20px;
      margin-bottom: 20px;
    }
    .progress-bar {
      width: 100%;
      height: 20px;
      background-color: #f0f0f0;
      border-radius: 10px;
      overflow: hidden;
    }
    .progress-fill {
      height: 100%;
      background-color: #4CAF50;
      width: 0%;
      transition: width 0.3s;
    }
    button {
      background-color: #4CAF50;
      color: white;
      padding: 10px 20px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin: 5px;
    }
    button:disabled {
      background-color: #cccccc;
      cursor: not-allowed;
    }
    button.cancel {
      background-color: #f44336;
    }
    .status {
      margin: 10px 0;
      padding: 10px;
      border-radius: 4px;
      background-color: #e0e0e0;
    }
    .status.success {
      background-color: #d4edda;
      color: #155724;
    }
    .status.error {
      background-color: #f8d7da;
      color: #721c24;
    }
    .status.info {
      background-color: #d1ecf1;
      color: #0c5460;
    }
    .file-info {
      background-color: #f8f9fa;
      padding: 15px;
      border-radius: 4px;
      margin: 10px 0;
    }
    .chunk-list {
      max-height: 200px;
      overflow-y: auto;
      border: 1px solid #ddd;
      padding: 10px;
      margin: 10px 0;
    }
  </style>
</head>
<body>
<h1>分片上传测试页面</h1>

<div class="container">
  <h2>后端配置</h2>
  <label for="backendUrl">后端服务器地址:</label>
  <input type="text" id="backendUrl" value="http://localhost:8080" style="width: 300px;">
</div>

<div class="container">
  <h2>文件选择</h2>
  <input type="file" id="fileInput" accept="video/*" />
  <button id="selectFileBtn">选择文件</button>
  <div id="fileInfo" class="file-info" style="display: none;"></div>
</div>

<div class="container">
  <h2>上传控制</h2>
  <button id="startUploadBtn" disabled>开始上传</button>
  <button id="pauseUploadBtn" disabled>暂停上传</button>
  <button id="resumeUploadBtn" disabled>继续上传</button>
  <button id="cancelUploadBtn" class="cancel" disabled>取消上传</button>

  <div class="progress-bar">
    <div id="progressFill" class="progress-fill"></div>
  </div>
  <div id="progressText">进度: 0%</div>
</div>

<div class="container">
  <h2>上传状态</h2>
  <div id="status" class="status">请选择文件开始上传</div>
</div>

<div class="container">
  <h2>已上传分片</h2>
  <button id="refreshChunksBtn" disabled>刷新分片列表</button>
  <div id="chunkList" class="chunk-list">暂无分片信息</div>
</div>

<script>
  // 全局变量
  let currentFile = null;
  let taskId = '';
  let uploadedChunks = new Set();
  let totalChunks = 0;
  let chunkSize = 5 * 1024 * 1024; // 5MB
  let currentChunk = 0;
  let isUploading = false;
  let isPaused = false;

  // DOM 元素
  const backendUrlInput = document.getElementById('backendUrl');
  const fileInput = document.getElementById('fileInput');
  const selectFileBtn = document.getElementById('selectFileBtn');
  const fileInfo = document.getElementById('fileInfo');
  const startUploadBtn = document.getElementById('startUploadBtn');
  const pauseUploadBtn = document.getElementById('pauseUploadBtn');
  const resumeUploadBtn = document.getElementById('resumeUploadBtn');
  const cancelUploadBtn = document.getElementById('cancelUploadBtn');
  const refreshChunksBtn = document.getElementById('refreshChunksBtn');
  const progressFill = document.getElementById('progressFill');
  const progressText = document.getElementById('progressText');
  const status = document.getElementById('status');
  const chunkList = document.getElementById('chunkList');

  // 事件监听器
  selectFileBtn.addEventListener('click', () => fileInput.click());
  fileInput.addEventListener('change', handleFileSelect);
  startUploadBtn.addEventListener('click', startUpload);
  pauseUploadBtn.addEventListener('click', pauseUpload);
  resumeUploadBtn.addEventListener('click', resumeUpload);
  cancelUploadBtn.addEventListener('click', cancelUpload);
  refreshChunksBtn.addEventListener('click', refreshChunkList);

  // 获取后端API基础URL
  function getApiBaseUrl() {
    const backendUrl = backendUrlInput.value.trim();
    if (!backendUrl) {
      return 'http://localhost:8080';
    }
    // 确保URL末尾没有斜杠
    return backendUrl.endsWith('/') ? backendUrl.slice(0, -1) : backendUrl;
  }

  // 处理文件选择
  function handleFileSelect(event) {
    const file = event.target.files[0];
    if (!file) return;

    currentFile = file;
    taskId = 'task_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
    showFileInfo(file);
    startUploadBtn.disabled = false;
    refreshChunksBtn.disabled = false;
    updateStatus('文件已选择，点击开始上传按钮进行分片上传', 'info');
  }

  // 显示文件信息
  function showFileInfo(file) {
    fileInfo.style.display = 'block';
    totalChunks = Math.ceil(file.size / chunkSize);

    fileInfo.innerHTML = `
                <strong>文件名:</strong> ${file.name}<br>
                <strong>文件大小:</strong> ${formatFileSize(file.size)}<br>
                <strong>分片大小:</strong> ${formatFileSize(chunkSize)}<br>
                <strong>总分片数:</strong> ${totalChunks}<br>
                <strong>任务ID:</strong> ${taskId}
            `;
  }

  // 格式化文件大小
  function formatFileSize(bytes) {
    if (bytes === 0) return '0 B';
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  // 更新状态显示
  function updateStatus(message, type = '') {
    status.textContent = message;
    status.className = 'status ' + type;
  }

  // 更新进度显示
  function updateProgress(percent) {
    progressFill.style.width = percent + '%';
    progressText.textContent = `进度: ${percent.toFixed(2)}%`;
  }

  // 初始化上传任务
  async function initUpload() {
    try {
      updateStatus('正在初始化上传任务...', 'info');

      // 模拟文件MD5 (实际应用中应该计算真实MD5)
      const fileMd5 = 'md5_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);

      const response = await fetch(`${getApiBaseUrl()}/api/upload/init`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          taskId: taskId,
          fileName: currentFile.name,
          fileSize: currentFile.size,
          fileMd5: fileMd5,
          chunkSize: chunkSize,
          totalChunks: totalChunks
        })
      });

      if (!response.ok) {
        throw new Error('初始化上传任务失败: ' + response.statusText);
      }

      const result = await response.json();
      updateStatus('上传任务初始化成功', 'success');
      return result;
    } catch (error) {
      updateStatus('初始化上传任务失败: ' + error.message, 'error');
      throw error;
    }
  }

  // 获取已上传分片列表
  async function getUploadedChunks() {
    try {
      const response = await fetch(`${getApiBaseUrl()}/api/upload/chunks/${taskId}`);
      if (!response.ok) {
        throw new Error('获取已上传分片列表失败: ' + response.statusText);
      }

      const result = await response.json();
      uploadedChunks = new Set(result.data || []);
      displayChunkList();
      return uploadedChunks;
    } catch (error) {
      updateStatus('获取已上传分片列表失败: ' + error.message, 'error');
      return new Set();
    }
  }

  // 刷新分片列表显示
  async function refreshChunkList() {
    if (!taskId) {
      updateStatus('请先选择文件', 'error');
      return;
    }

    await getUploadedChunks();
    updateStatus(`已刷新分片列表，已上传 ${uploadedChunks.size} 个分片`, 'info');
  }

  // 显示分片列表
  function displayChunkList() {
    if (uploadedChunks.size === 0) {
      chunkList.innerHTML = '暂无已上传分片';
      return;
    }

    const chunks = Array.from(uploadedChunks).sort((a, b) => a - b);
    chunkList.innerHTML = `
                <p>已上传分片: ${chunks.length}/${totalChunks}</p>
                <p>分片序号: ${chunks.join(', ')}</p>
            `;
  }

  // 上传单个分片
  async function uploadChunk(chunkNumber) {
    return new Promise((resolve, reject) => {
      const start = chunkNumber * chunkSize;
      const end = Math.min(start + chunkSize, currentFile.size);
      const chunk = currentFile.slice(start, end);

      const formData = new FormData();
      formData.append('taskId', taskId);
      formData.append('chunkNumber', chunkNumber.toString());
      formData.append('chunk', chunk);
      formData.append('chunkSize', chunkSize.toString());
      formData.append('currentChunkSize', (end - start).toString());

      const xhr = new XMLHttpRequest();

      xhr.upload.addEventListener('progress', (e) => {
        if (e.lengthComputable) {
          const chunkProgress = (e.loaded / e.total) * 100;
          const overallProgress = ((chunkNumber + chunkProgress / 100) / totalChunks) * 100;
          updateProgress(overallProgress);
        }
      });

      xhr.addEventListener('load', () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(JSON.parse(xhr.responseText));
        } else {
          reject(new Error('上传分片失败: ' + xhr.statusText));
        }
      });

      xhr.addEventListener('error', () => {
        reject(new Error('网络错误'));
      });

      xhr.open('POST', `${getApiBaseUrl()}/api/upload/chunk`);
      xhr.send(formData);
    });
  }

  // 完成上传
  async function completeUpload() {
    try {
      updateStatus('已经完成上传', 'info');

      const response = await fetch(`${getApiBaseUrl()}/api/upload/complete`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          taskId: taskId,
          fileName: currentFile.name,
          fileSize: currentFile.size
        })
      });

      if (!response.ok) {
        throw new Error('完成上传失败: ' + response.statusText);
      }

      const result = await response.json();
      updateStatus('上传完成!', 'success');
      return result;
    } catch (error) {
      updateStatus('完成上传失败: ' + error.message, 'error');
      throw error;
    }
  }

  // 开始上传
  async function startUpload() {
    if (!currentFile) {
      updateStatus('请先选择文件', 'error');
      return;
    }

    if (isUploading) {
      updateStatus('上传已在进行中', 'error');
      return;
    }

    try {
      isUploading = true;
      isPaused = false;
      startUploadBtn.disabled = true;
      pauseUploadBtn.disabled = false;
      cancelUploadBtn.disabled = false;

      // 初始化上传任务
      await initUpload();

      // 获取已上传分片
      await getUploadedChunks();

      // 逐个上传分片
      for (let i = 0; i < totalChunks; i++) {
        // 检查是否需要暂停
        while (isPaused) {
          await new Promise(resolve => setTimeout(resolve, 100));
        }

        // 检查是否已取消
        if (!isUploading) {
          return;
        }

        // 跳过已上传的分片
        if (uploadedChunks.has(i)) {
          const progress = ((i + 1) / totalChunks) * 100;
          updateProgress(progress);
          updateStatus(`跳过分片 ${i + 1}/${totalChunks} (已上传)`, 'info');
          continue;
        }

        // 上传当前分片
        updateStatus(`正在上传分片 ${i + 1}/${totalChunks}...`, 'info');
        await uploadChunk(i);
        uploadedChunks.add(i);

        // 更新进度
        const progress = ((i + 1) / totalChunks) * 100;
        updateProgress(progress);
        displayChunkList();
      }

      // 完成上传
      const result = await completeUpload();
      updateStatus(`上传成功! 视频ID: ${result.data.id}`, 'success');

      // 重置状态
      resetUploadState();

    } catch (error) {
      updateStatus('上传失败: ' + error.message, 'error');
      isUploading = false;
    }
  }

  // 暂停上传
  function pauseUpload() {
    if (!isUploading) {
      updateStatus('没有正在进行的上传任务', 'error');
      return;
    }

    isPaused = true;
    pauseUploadBtn.disabled = true;
    resumeUploadBtn.disabled = false;
    updateStatus('上传已暂停', 'info');
  }

  // 继续上传
  function resumeUpload() {
    if (!isUploading) {
      updateStatus('没有暂停的上传任务', 'error');
      return;
    }

    isPaused = false;
    pauseUploadBtn.disabled = false;
    resumeUploadBtn.disabled = true;
    updateStatus('继续上传...', 'info');
  }

  // 取消上传
  function cancelUpload() {
    isUploading = false;
    isPaused = false;
    resetUploadState();
    updateStatus('上传已取消', 'info');
  }

  // 重置上传状态
  function resetUploadState() {
    isUploading = false;
    isPaused = false;
    startUploadBtn.disabled = false;
    pauseUploadBtn.disabled = true;
    resumeUploadBtn.disabled = true;
    cancelUploadBtn.disabled = true;
  }
</script>
</body>
</html>
